@page "/dashboard"
@using System.Text.RegularExpressions
@using Radzen.Blazor.Rendering
@using RadzenBlazorDemos.Models.GitHub
@using RadzenBlazorDemos.Services
@inject GitHubService GitHub

<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" Class="rz-pt-8">
    Blazor GitHub Issues
</RadzenText>

<RadzenStack Gap="1rem" Style="position: relative">
    <RadzenCard Visible=@fetchingData Style="z-index: 3; text-align: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, .5)">
        <RadzenCard Visible=@(error == null) Class="rz-my-12 rz-mx-auto rz-p-12" Style="width: 80%">
            <RadzenText TextStyle="TextStyle.H6" Class="rz-my-12">Fetching data from GitHub...</RadzenText>
            <RadzenText TextStyle="TextStyle.Subtitle1">Page @currentPage of @totalPages</RadzenText>
            <RadzenProgressBar Value=@currentPage Max=@totalPages ShowValue="false" Style="display: inline-block; width: 180px; margin-top: 16px" />
        </RadzenCard>
        <RadzenCard Visible=@(error != null) Class="rz-my-12 rz-mx-auto rz-p-12" Style="width: 80%">
            <RadzenText TextStyle="TextStyle.H6" Class="rz-my-8">An error has occurred: @error. Try reloading your browser.</RadzenText>
        </RadzenCard>
    </RadzenCard>
    <RadzenRow JustifyContent="JustifyContent.SpaceBetween" AlignItems="AlignItems.Center" Class="rz-mb-4">
        <RadzenColumn Size="12" SizeSM="9">
            <RadzenText TextStyle="TextStyle.Subtitle1" Class="rz-m-0">
                Sample dashboard that uses data from the <a href="https://github.com/dotnet/aspnetcore/issues" target="_blank">ASP.NET GitHub repository</a>. Data is updated 24 hours.
            </RadzenText>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeSM="3">
            <RadzenDropDown Style="width: 100%" Data=@issueStates @bind-Value="selectedState" Change=@FilterIssues>
                <Template Context="issueState">
                    @Enum.GetName(typeof(IssueState), issueState)
                </Template>
            </RadzenDropDown>
        </RadzenColumn>
    </RadzenRow>
    <RadzenRow>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="3">
            <RadzenCard Style="position: relative;">
                <RadzenText TextStyle="TextStyle.H6">Open Issues</RadzenText>
                <RadzenText TextStyle="TextStyle.H2" Class="rz-color-success" Style="position: absolute; right: 1.5rem; top: 1rem;">@openIssues?.Count()</RadzenText>
                <RadzenChart Style="width: 100%; height: 120px; margin-bottom: -30px;">
                    <RadzenColumnSeries Data=@openIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Issues" Fill="var(--rz-success)" />
                    <RadzenValueAxis Visible="false" />
                    <RadzenCategoryAxis Visible="false" />
                    <RadzenColumnOptions Margin="10" />
                    <RadzenLegend Visible="false" />
                </RadzenChart>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="3">
            <RadzenCard Style="position: relative;">
                <RadzenText TextStyle="TextStyle.H6">Closed Issues</RadzenText>
                <RadzenText TextStyle="TextStyle.H2" Class="rz-color-danger" Style="position: absolute; right: 1.5rem; top: 1rem;">@closedIssues?.Count()</RadzenText>
                <RadzenChart Style="width: 100%; height: 120px; margin-bottom: -30px;">
                    <RadzenColumnSeries Data=@closedIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Issues" Fill="var(--rz-danger)" />
                    <RadzenValueAxis Visible="false" />
                    <RadzenCategoryAxis Visible="false" />
                    <RadzenColumnOptions Margin="10" />
                    <RadzenLegend Visible="false" />
                </RadzenChart>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="3">
            <RadzenCard Style="position: relative;">
                <RadzenText TextStyle="TextStyle.H6">All Issues</RadzenText>
                <RadzenText TextStyle="TextStyle.H2" Style="position: absolute; right: 1.5rem; top: 1rem;">@issues?.Count()</RadzenText>
                <RadzenChart Style="width: 100%; height: 120px; margin-bottom: -30px;">
                    <RadzenLineSeries Data=@openIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Open Issues" Smooth="true" Stroke="var(--rz-success)" />
                    <RadzenLineSeries Data=@closedIssuesByDate ValueProperty="Count" CategoryProperty="Week" Title="Closed Issues" Smooth="true" Stroke="var(--rz-danger)" />
                    <RadzenValueAxis Visible="false" />
                    <RadzenCategoryAxis Visible="false" />
                    <RadzenLegend Visible="false" />
                </RadzenChart>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="3">
            <RadzenCard>
                <RadzenText TextStyle="TextStyle.H6">Progress</RadzenText>
                <RadzenArcGauge Style="width: 100%; height: 120px; margin-top: -30px;">
                    <RadzenArcGaugeScale Max="1" Y="0.8" Radius="2">
                        <RadzenArcGaugeScaleValue Value=@closeRatio FormatString="{0:P0}" />
                    </RadzenArcGaugeScale>
                </RadzenArcGauge>
            </RadzenCard>
        </RadzenColumn>
    </RadzenRow>
    <RadzenRow>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="4">
            <RadzenCard Style="height: 280px">
                <RadzenText TextStyle="TextStyle.H6">Top Issue Labels</RadzenText>
                <RadzenChart Style="width: 100%; height: 200px">
                    <RadzenDonutSeries Data=@labelGroups CategoryProperty="Label" ValueProperty="Count" Title="Issues" Fills=@labelColors />
                </RadzenChart>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="4">
            <RadzenCard Style="height: 280px">
                <RadzenText TextStyle="TextStyle.H6">Top Contributors</RadzenText>
                <RadzenChart Style="width: 100%; height: 200px">
                    <RadzenPieSeries Data=@openByGroups CategoryProperty="Name" ValueProperty="Count" Title="Issues" />
                </RadzenChart>
            </RadzenCard>
        </RadzenColumn>
        <RadzenColumn Size="12" SizeMD="6" SizeLG="4">
            <RadzenCard Style="height: 280px">
                <RadzenText TextStyle="TextStyle.H6">Most Active Member</RadzenText>
                <div Class="rz-text-align-center">
                    <img Style="width: 120px; border-radius: 50%; margin: 20px 0;" src=@mostActiveMember?.AvatarUrl />
                    <h4>
                        @mostActiveMember?.Login
                    </h4>
                </div>
            </RadzenCard>
        </RadzenColumn>
    </RadzenRow>
    <RadzenRow>
        <RadzenColumn Size="12">
            <RadzenCard>
                <RadzenText TextStyle="TextStyle.H6">Issue List</RadzenText>
                <RadzenDataGrid Data=@filteredIssues Style="height: 500px" AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" AllowSorting="true">
                    <Columns>
                        <RadzenDataGridColumn TItem="Issue" Title="User" Width="200px" Property="User.Login" FilterValue="@selectedUser?.Login">
                            <FilterTemplate>
                                <RadzenDropDown AllowClear="true" AllowFiltering="true" Data=@users TextProperty="Login" @bind-Value="selectedUser" Change=@FilterIssues>
                                    <Template Context="user">
                                        <div Style="white-space: nowrap">
                                            <img Style="width: 24px; height: 24px; border-radius: 50%; margin-right: 8px;" src=@user.AvatarUrl />@user.Login
                                        </div>
                                    </Template>
                                </RadzenDropDown>
                            </FilterTemplate>
                            <Template Context="issue">
                                <img Style="width: 32px; height: 32px; border-radius: 50%; margin-right: 8px; border: 1px solid #cccccc;" src=@issue.User.AvatarUrl /><b>@issue.User.Login</b>
                            </Template>
                        </RadzenDataGridColumn>
                        <RadzenDataGridColumn TItem="Issue" Property="Title" Title="Title" Width="400px">
                            <Template Context="issue">
                                <RadzenLink Path=@issue.Url Text=@issue.Title Target="_blank" />
                            </Template>
                        </RadzenDataGridColumn>
                        <RadzenDataGridColumn TItem="Issue" Property="State" Title="State" Width="120px">
                            <FilterTemplate>
                                <RadzenDropDown Style="width: 100%" Data=@issueStates @bind-Value="selectedState" Change=@FilterIssues>
                                    <Template Context="issueState">
                                        @issueState.ToString()
                                    </Template>
                                </RadzenDropDown>
                            </FilterTemplate>
                            <Template Context="issue">
                                @if (issue.State == IssueState.Open)
                                {
                                    <RadzenBadge Text="Open" IsPill="true" Variant="Variant.Outlined" BadgeStyle="BadgeStyle.Success" />
                                }
                                else
                                {
                                    <RadzenBadge Text="Closed" IsPill="true" Variant="Variant.Outlined" BadgeStyle="BadgeStyle.Info" />
                                }
                            </Template>
                        </RadzenDataGridColumn>
                        <RadzenDataGridColumn TItem="Issue" Title="Labels" Sortable="false">
                            <FilterTemplate>
                                <RadzenDropDown Style="width: 100%" AllowClear="true" AllowFiltering="true" Multiple="true" Data=@labels @bind-Value="selectedLabels" Change=@FilterIssues>
                                    <Template Context="label">
                                        @Regex.Replace(label, ":\\w+:", "")
                                    </Template>
                                </RadzenDropDown>
                            </FilterTemplate>

                            <Template Context="issue">
                                @foreach(var label in issue.Labels)
                                {
                                    <span Class="rz-badge rz-badge-pill rz-mr-1 rz-mb-1" Style="background-color: #@label.Color">@Regex.Replace(label.Name, ":\\w+:", "")</span>
                                }
                            </Template>
                        </RadzenDataGridColumn>
                    </Columns>
                </RadzenDataGrid>
            </RadzenCard>
        </RadzenColumn>
    </RadzenRow>
</RadzenStack>

@code {
    IEnumerable<Issue> issues;
    IEnumerable<Issue> openIssues;
    IEnumerable<Issue> closedIssues;

    class IssueGroup
    {
        public int Count { get; set; }
        public DateTime Week { get; set; }
    }

    class LabelGroup
    {
        public int Count { get; set; }
        public string Label { get; set; }
        public string Color { get; set; }
    }

    class UserGroup
    {
        public int Count { get; set; }
        public string Name { get; set; }
    }

    IEnumerable<IssueGroup> openIssuesByDate;
    IEnumerable<IssueGroup> closedIssuesByDate;
    IEnumerable<LabelGroup> labelGroups;
    IEnumerable<UserGroup> openByGroups;
    IEnumerable<User> users;
    IEnumerable<Issue> filteredIssues;
    IEnumerable<string> labels;
    IEnumerable<string> selectedLabels;
    IEnumerable<IssueState> issueStates = Enum.GetValues(typeof(IssueState)).Cast<IssueState>();
    IEnumerable<string> labelColors;
    IssueState selectedState = IssueState.All;
    User mostActiveMember;
    User selectedUser;
    double closeRatio = 0;
    int currentPage = 0;
    int totalPages = 0;
    bool fetchingData = false;
    string error = null;

    class UserComparer : IEqualityComparer<User>
    {
        public bool Equals(User x, User y)
        {
            return x.Login.Equals(y.Login);
        }

        public int GetHashCode(User user)
        {
            return user.Login.GetHashCode();
        }
    }

    class LabelComparer : IEqualityComparer<Label>
    {
        public bool Equals(Label x, Label y)
        {
            return x.Name.Equals(y.Name);
        }

        public int GetHashCode(Label user)
        {
            return user.Name.GetHashCode();
        }
    }

    void FilterIssues()
    {
        filteredIssues = issues.OrderByDescending(issue => issue.CreatedAt);

        if (selectedUser != null)
        {
            filteredIssues = issues.Where(issue => issue.User.Login == selectedUser.Login);
        }

        if (selectedLabels != null)
        {
            foreach (var selectedLabel in selectedLabels)
            {
                filteredIssues = filteredIssues.Where(issue => issue.Labels.Any(label => label.Name == selectedLabel));
            }
        }

        if (selectedState != IssueState.All)
        {
            filteredIssues = filteredIssues.Where(issue => issue.State == selectedState);
        }
    }

    void OnProgress(FetchProgressEventArgs args)
    {
        currentPage = args.Current;
        totalPages = args.Total;

        StateHasChanged();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            GitHub.OnProgress += OnProgress;
            fetchingData = true;
            try
            {
                issues = await GitHub.GetIssues(DateTime.Now);
                filteredIssues = issues.OrderByDescending(issue => issue.CreatedAt);
                openIssues = issues.Where(issue => issue.State == IssueState.Open);
                closedIssues = issues.Where(issue => issue.State == IssueState.Closed);

                closeRatio = closedIssues.Count() / (double)issues.Count();

                openIssuesByDate = openIssues.GroupBy(issue => issue.CreatedAt.EndOfWeek())
                        .Select(group => new IssueGroup
                        {
                            Week = group.Key,
                            Count = group.Count()
                        });

                closedIssuesByDate = closedIssues.GroupBy(issue => issue.ClosedAt.Value.EndOfWeek())
                        .Select(group => new IssueGroup
                        {
                            Week = group.Key,
                            Count = group.Count()
                        });

                labels = issues.SelectMany(issue => issue.Labels).Select(label => label.Name).Distinct();

                labelGroups = issues.SelectMany(issue => issue.Labels)
                                    .GroupBy(label => label, new LabelComparer())
                                    .Select(group => new LabelGroup { Label = Regex.Replace(group.Key.Name, ":\\w+:", ""), Color = $"#{group.Key.Color}", Count = group.Count() })
                                    .Where(group => group.Label != "area-blazor")
                                    .OrderByDescending(group => group.Count)
                                    .Take(5);

                labelColors = labelGroups.Select(label => label.Color);

                openByGroups = issues.GroupBy(issue => issue.User.Login)
                                    .Select(group => new UserGroup { Name = group.Key, Count = group.Count() })
                                    .OrderByDescending(group => group.Count)
                                    .Take(7);

                mostActiveMember = issues.SelectMany(issue => issue.Assignees)
                                    .GroupBy(assignee => assignee, new UserComparer())
                                    .Select(group => new { User = group.Key, Count = group.Count() })
                                    .OrderByDescending(group => group.Count)
                                    .Select(group => group.User)
                                    .FirstOrDefault();

                users = issues.Select(issue => issue.User)
                            .Distinct(new UserComparer());

                error = null;
                fetchingData = false;
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }

            StateHasChanged();
        }
    }
}